package com.flying.acls.adapter.spring;

import com.flying.acls.domain.context.DoExprContextBuilder;
import com.flying.acls.domain.context.ExpressionContext;
import com.flying.acls.domain.permission.ClassPermissionBuilder;
import com.flying.acls.domain.resource.DoPatternBasedResourceBuilder;
import com.flying.acls.domain.service.AclService;
import com.flying.acls.domain.sid.PatternBasedSidBuilder;
import com.flying.acls.model.PermsExpr;
import com.flying.acls.model.PermsType;
import com.flying.acls.model.ResExpr;
import com.flying.acls.model.SidExpr;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;

import java.io.Serializable;
import java.util.List;

@Slf4j
@Setter
public class DoPermissionEvaluator implements PermissionEvaluator {
    @Autowired
    private DoExprContextBuilder contextBuilder;
    @Autowired
    private DoPatternBasedResourceBuilder resourceBuilder;
    @Autowired
    private PatternBasedSidBuilder sidBuilder;
    @Autowired
    private ClassPermissionBuilder permissionBuilder;
    @Autowired
    private AclService aclService;

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if (targetDomainObject == null) return false;
        String className = targetDomainObject.getClass().getName();
        if (!resourceBuilder.support(className)) return false;

        ExpressionContext context = contextBuilder.build(authentication, targetDomainObject, permission);
        List<ResExpr> requiredResExprs = resourceBuilder.build(context, className);

        return checkPermission(authentication, context, requiredResExprs, permission);
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        if (!resourceBuilder.support(targetType)) return false;

        ExpressionContext context = contextBuilder.build(authentication, targetId, targetType, permission);
        List<ResExpr> requiredResExprs = resourceBuilder.build(context, targetType);

        return checkPermission(authentication, context, requiredResExprs, permission);
    }

    private boolean checkPermission(Authentication authentication, ExpressionContext context, List<ResExpr> requiredResExprs, Object permission) {
        List<SidExpr> sidExprs = sidBuilder.build(context, AuthenticationExtractor.getPrincipalName(authentication), AuthenticationExtractor.getRoles(authentication));
        List<PermsExpr> requiredPermsExpr = permissionBuilder.build(PermsType.CRUD, permission);
        return aclService.isGranted(context, requiredResExprs, sidExprs, requiredPermsExpr);
    }
}